package xu.jiang.report.dealer.report;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Sheet;
import xu.jiang.report.Param;
import xu.jiang.report.R;
import xu.jiang.report.dealer.ISQLRunner;
import xu.jiang.report.dealer.layer.LoopLayer;
import xu.jiang.report.dealer.sheet.SheetDealer;
import xu.jiang.report.util.SQLUtil;
import xu.jiang.report.util.report.RegexUtil;
import xu.jiang.report.util.report.ReportUtil;

import java.io.IOException;
import java.util.*;

/**
 * 分页解析器
 *
 * @see
 * @author jx
 * @date 2017年5月4日 下午5:39:42
 */
public class PagingAnalyzer {

    private final Param param;

    // 分页对象
    private String objectName = null;
    // 分页DB视图
    private String view4DB = null;

    private int pageIndex = 0;

    public PagingAnalyzer(Param param) {
        this.param = param;
        // this.pagingBrothersColumns = pagingColumns;
    }

    /**
     * 循环替换分页参数， 通过分页替换符将每个sheet页替换为能独立处理的sheet，替换完毕后，每个sheet与sheet参数无关
     *
     * @throws IOException
     */
    public List<SheetDealer> page2Sheet(ISQLRunner sqlRunner,String sheetname_modelSheet) throws IOException {

        /*
         * 检测是否已经获取了模板sheet名称
         * 当sheetname_modelSheet不为空时，说明ui.complexPageColumn涂改了模板初始sheetname；
         * 原因：由于Excel的sheetName有字数限制，建议采用ui.complexPageColumn彻底涂改模板最初的默认值
         * 这个where部分是要拼凑在分页sql中的 sheetname_modelSheet形如：{w.a},{w.b}<{w.b} is
         * null>
         */
        if (StringUtils.isBlank(sheetname_modelSheet)) {

            sheetname_modelSheet = this.param.mainSheet.getSheetName();
        }

        if (!sheetname_modelSheet.matches(R.OBJECT_PROPERTY_REGEX + ".*")) {// 没有分页

            return this.getSheets4noPaging();
        }
        // -------解析sheetname------start--------------------------

        /*
         * 实际存储分页属性 ，初始时给与默认值sheetname_modelSheet（形如{w.a},{w.b}<{w.b} is
         * null>） 故我们后面要去掉<>后面部分，且分页属性可能包含多个分页属性，逗号分隔
         */
        String keys4Sheet = sheetname_modelSheet;

        // 准备获取分页属性后面的sql部分，即<>部分，这部分是分页的where语句
        String pagingSql4WherePiece = "";
        // 判断是否存在where语句的条件：即存在<>
        if (sheetname_modelSheet.contains("<") && sheetname_modelSheet.contains(">")) {
            // 已经去除where部分的分页属性字符串
            keys4Sheet = sheetname_modelSheet.substring(0, sheetname_modelSheet.indexOf("<"));
            pagingSql4WherePiece = sheetname_modelSheet.replace(keys4Sheet, "");
            // 去掉首尾的<>
            pagingSql4WherePiece = pagingSql4WherePiece.substring(1, pagingSql4WherePiece.length() - 1);
            //剥去替换符外衣，变成真正的wheresql
            pagingSql4WherePiece = " and " + this.takeOffRegex(pagingSql4WherePiece, param);
        }
        // ------解析sheetname------end----------------

        String sheetNameKey = null;
        // 将包含多值的分页属性分隔开来
        String[] array4Key = keys4Sheet.split(",");
        int length = array4Key.length;
        for (int i = 0; i < length; i++) {
            String pageColumn = array4Key[i];

            // 放入分页属性集合中(这里将分页属性的形如{ne.code}->{sheet.code}的原因是避免后期存在重复的{sheet.code},属性重复会造成sql错误)
            this.param.pagingColumnContainer.add(pageColumn.replaceAll(R.OBJECT_PROPERTY_REGEX_CONTENT_PREFIX,
                    R.SHEET_REGEX_CONTENT_PREFIX_STRING));

            // 存储分页时使用的对象
            if (i == 0) {
                // 剥去外衣后的值：{w.code}->w.code
                String pureColumn = RegexUtil.takeOffPropertyRegex(pageColumn);
                String[] tempValue = pureColumn.split("\\.");
                // 取第一个值来获取分页对象名称
                this.objectName = tempValue[0];
            }
            //默认去最后一个作为分页名称的默认根值
            if(i==length-1) {
                String pureColumn = RegexUtil.takeOffPropertyRegex(pageColumn);
                String[] tempValue = pureColumn.split("\\.");
                sheetNameKey = tempValue[1];
            }
        }

        // 找到对应的真实的数据库表名或视图（去属性文件里找到对应关系）
        this.view4DB = ReportUtil.getRealViewName(this.objectName);
        // this.view4DB = this.view4DB.contains(" ")?" ("+this.view4DB+")
        // ":this.view4DB;

        // 获取分页数据
        List<Map<String, String>> dataList_paging = this.getPagingData(sqlRunner, pagingSql4WherePiece);
        if (dataList_paging == null || dataList_paging.size() <= 0) {// 没数据，那么没有分页；
            return getBlankSheet();
        }

        // 标记分页，这回真的确定要分页了
        this.param.isPaging = true;

        List<SheetDealer> sheetDealers = new ArrayList<SheetDealer>();


        // List<String> pageColumns = this.param.pagingColumnContainer.getOrInitializeCachePigingColumns();
        // 获取每一页的数据集,每一页就是一个sheet
        for (; pageIndex < dataList_paging.size(); pageIndex++) {

            Map<String, String> pagingParamValues = dataList_paging.get(pageIndex);


            // test
//				for (Map.Entry<String, String> entry : pagingParamValues.entrySet()) {
//					System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
//				}

            /*
             * 创建sheetDealer，并添加到集合中 注意：sheetDealer中的模板是主模板的克隆sheet
             */
            SheetDealer sheetDealer = new SheetDealer(pageIndex, AbstractReportDealer.cloneSheet(this.param.mainSheet), pagingParamValues,
                    this.param);
            sheetDealer.setSheetNameRoot(pagingParamValues.get(sheetNameKey));
            sheetDealers.add(sheetDealer);
        }

        return sheetDealers;
    }

    /**
     * 没有分页
     *
     * @return 返回没有分页的Report的sheet集合
     */
    private List<SheetDealer> getSheets4noPaging() {
        /*
         * 更加模板克隆一个新的模板作为数据填写的蓝本
         */
        SheetDealer oneSheet = new SheetDealer(pageIndex, AbstractReportDealer.cloneSheet(this.param.mainSheet), null, this.param);
        List<SheetDealer> olist = new ArrayList<SheetDealer>();
        olist.add(oneSheet);
        return olist;
    }

    /**
     * 没有分页
     *
     * @return 返回没有分页的Report的sheet集合
     */
    private List<SheetDealer> getBlankSheet() {
        ArrayList<SheetDealer> blankSheets = new ArrayList<SheetDealer>(1);

        Sheet sheet = AbstractReportDealer.cloneSheet(this.param.mainSheet);
        //插入空模板
        Sheet blankSheet  = SheetDealer.BlankSheet.makeAllCellsBlank("(以下是空白行)", sheet);
        blankSheets.add(new SheetDealer(pageIndex,blankSheet, null, this.param));
        return blankSheets;
    }

    /**
     * 获取分页数据
     *
     */
    private List<Map<String, String>> getPagingData(ISQLRunner sqlRunner, String whereOfGroup) {
        /*
         * 将同胞属性集合拼凑成sql的select片段
         */
        List<String> allPagingColumns = this.param.pagingColumnContainer.getOrInitializeCachePigingColumns();
        String[] arr_columns = allPagingColumns.toArray(new String[allPagingColumns.size()]);
        String partOfSql4names = StringUtils.join(arr_columns, ",");

        /*
         * 模板上的分页属性和UI参数匹配时的where部分
         */
        StringBuffer sql_param_paging = new StringBuffer();

        // 如果分页属性在ui参数中存在值，那么应该将分页属性的ui值也放入where部分，这里我们使用in语句实现
        List<String> pagingColumnsOfFiltering = this.param.pagingColumnContainer
                .getOrInitializeCachePigingColumnsOfwhereSQL4layer();
        for (String column : pagingColumnsOfFiltering) {

            // 找到ui中的where部分
            String pageColumn4UI = "ui." + column;
            String param_paging4Where = this.param.uiParams.get(pageColumn4UI);

            if (!StringUtils.isBlank(param_paging4Where)) {
                sql_param_paging.append(" and " + column + " in("
                        + SQLUtil.filter2SqlOfIn(Arrays.asList(param_paging4Where.split(","))) + ")");
                // 必须移除被分页属性使用过的ui属性
                this.param.uiParams.remove(pageColumn4UI);
            }
        }

        /*
         * 形如：select distinct a.A,a.B from view a group by a.A,a.B
         * 第一个值默认为分页属性，后面的为同胞属性 由于分页属性可能不是对象的主键，select后可能存在重复值，
         * 而distinct无法对多个属性操作，故采用group by
         *
         * 之所以增加个冗余列xyz123，是为了hibernate的数据集大于1个， 这样就可以统一用List<Object[]>接收了
         *
         */
        StringBuffer sql = new StringBuffer("select distinct " + partOfSql4names + ",'' as xyz123  from "
                + this.view4DB + " " + this.objectName);

        /*
         * where部分
         */
        sql.append(" where 1=1 " + sql_param_paging.toString() + whereOfGroup);
        // UI端的 where by部分
        String whereBySql = this.param.uiParams.remove("ui." + R.Sheet.WHERE_SQL);
        if (!StringUtils.isBlank(whereBySql)) {

            sql.append(" and " + this.takeOffRegex(whereBySql, param));

        }

        /*
         * group by部分
         */
        String groupBySql = this.param.uiParams.remove("ui." + R.Sheet.GROUP_BY_SQL);
        if (!StringUtils.isBlank(groupBySql)) {

            sql.append(" group by " + this.takeOffRegex(groupBySql, param));
        }

        /*
         * order by部分
         */
        String orderSql = this.param.uiParams.remove("ui." + R.Sheet.ORDER_BY_SQL);
        if (!StringUtils.isBlank(orderSql)) {

            sql.append(
                    " order by " + this.takeOffRegex(orderSql, param).replace("+", " asc ").replace("-", " desc "));
        }
        /*
         * 获取分页的数据集
         */
        List<Object[]> dataList_paging = sqlRunner.createSQLQuery(sql.toString(),allPagingColumns);

        /*
         * 拼凑结构体
         */
        List<Map<String, String>> mapResult = new ArrayList<>();
        for (Object[] dataByPage: dataList_paging) {
            /*
             * 存储每页（sheet）的分页属性和同胞属性的(key-value)值对
             */
            Map<String, String> pagingParamValues = new HashMap<String, String>();

            for (int i = 0; i < allPagingColumns.size(); i++) {
                //分页属性的值
                String columnValue = dataByPage[i] == null ? "" : String.valueOf(dataByPage[i].toString());
                //分页属性的名称
                String columnName = allPagingColumns.get(i);
                // 将值填到预先存好的同胞属性（包括分页属性）
                pagingParamValues.put(columnName, columnValue);
            }
            mapResult.add(pagingParamValues);
        }
        return mapResult;
    }

    /**
     * 脱掉UI替换符、系统替换符、属性替换符
     *
     * @param column
     * @param param
     * @return
     */
    private String takeOffRegex(String column, Param param) {
        // 替换所有系统参数
        String c1 = RegexUtil.takeOffSysRegex(column, this.param, false);
        /*
         * 替换UI参数
         */
        String c2 = RegexUtil.takeOffUIRegex(c1, this.param, false);
        /*
         * 替换对象属性
         */
        String c3 = RegexUtil.takeOffPropertyRegex(c2);
        return c3;

    }


}

